package com.wxmit.ot.gen.service;

import com.wxmit.ot.base.core.OtBaseServiceImpl;
import com.wxmit.ot.exception.BaseException;
import com.wxmit.ot.gen.constant.CodeTemplateParamConstant;
import com.wxmit.ot.gen.domain.GenGroupItem;
import com.wxmit.ot.gen.domain.GenTable;
import com.wxmit.ot.gen.domain.dto.GenCodePreviewDto;
import com.wxmit.ot.gen.domain.dto.GenTableInitDto;
import com.wxmit.ot.gen.mapper.GenTableMapper;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.util.Assert;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfig;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * GenGroupTableService
 *
 * @author huangxiong
 * @version V1.0
 * @date 2023/2/22 10:44
 */
@Service
@RequiredArgsConstructor
public class GenTableService extends OtBaseServiceImpl<GenTableMapper, GenTable> {


    private final GenTableColumnService genTableColumnService;

    private final GenGroupService genGroupService;

    private final GenGroupItemService genGroupItemService;

    private final FreeMarkerConfig freeMarkerConfig;

    public GenTable initGenTable(GenTableInitDto genTableInitDto) {
        return GenTable.buildGenTableByInitDto(genTableInitDto);
    }

    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
    public Long addByInitGenTable(GenTableInitDto genTableInitDto) {
        GenTable genTable = initGenTable(genTableInitDto);
        boolean save = save(genTable);
        Assert.isTrue(save,"新增失败！请刷新后重试");
        return genTable.getId();
    }
    @Override
    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
    public boolean save(GenTable entity) {
        boolean result = super.save(entity);

        if(result){
            genTableColumnService.saveBatch(entity.getColumns());
        }

        return result;
    }

    @Override
    public boolean updateById(GenTable entity) {
        boolean result = super.updateById(entity);
        if(result){
            genTableColumnService.saveOrUpdateBatch(entity.getColumns());
        }

        return result;
    }

    public GenTable getByIdWithColumn(Long id){
        GenTable genTable = getById(id);
        genTable.setColumns(genTableColumnService.getByTableId(id));
        return genTable;
    }

    public void genCodeById(Long id, OutputStream outputStream) throws IOException {
        GenTable genTable = getByIdWithColumn(id);
        Map<String, Object> codeTemplateParamMap = CodeTemplateParamConstant.buildByGenTable(genTable);
        // 获取模版组 - 文件
        List<GenGroupItem> genGroupItems = genGroupItemService.listByGroupId(genTable.getGroupId());

        try ( ZipOutputStream zip = new ZipOutputStream(outputStream);) {
            genGroupItems.filter(it -> !it.getCode().isNullOrBlank()).forEach(it -> {
                try {
                    // do write
                    String fileName = processTemplateCodeIntoString("genTemplateName",it.getName(),codeTemplateParamMap);
                    zip.putNextEntry(
                            new ZipEntry(
                                    processTemplateCodeIntoString("genTemplatePath",it.getPath(),codeTemplateParamMap) + fileName
                            )
                    );
                    String code = processTemplateCodeIntoString(it.getName(),it.getCode(),codeTemplateParamMap) ;
                    zip.write(code.getBytes(StandardCharsets.UTF_8));
                    // flush
                    zip.flush();
                    zip.closeEntry();
                } catch (IOException e) {
                    log.error("代码生成io异常：：：：",e);
                    throw new BaseException("代码生成io异常",e);
                }
            });
        }
    }

    public List<GenCodePreviewDto> preview(Long id) {
        GenTable genTable = getByIdWithColumn(id);
        Map<String, Object> codeTemplateParamMap = CodeTemplateParamConstant.buildByGenTable(genTable);
        // 获取模版组 - 文件
        List<GenGroupItem> genGroupItems = genGroupItemService.listByGroupId(genTable.getGroupId());
        return genGroupItems.filter(it -> !it.getCode().isNullOrBlank()).map(it -> {
            // do write
            String fileName = processTemplateCodeIntoString("genTemplateName", it.getName(), codeTemplateParamMap);
           String path =  processTemplateCodeIntoString("genTemplatePath",it.getPath(),codeTemplateParamMap);
           String code = processTemplateCodeIntoString(it.getName(),it.getCode(),codeTemplateParamMap) ;
           return new GenCodePreviewDto(it.getId(),path,fileName,code,it.getLanguage());
        }).toList();
    }

    @Override
    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
    public boolean removeByIds(Collection<?> list) {
        genTableColumnService.removeByTableIds(list);
        return super.removeByIds(list);
    }

    /**
     *  将模版代码转换成 string 值
     * @param templateName 模版名（随意）
     * @param templateCode 模版代码
     * @param paramMap 模版参数
     * @return 模版参数对应的string值
     */
    private String processTemplateCodeIntoString(String templateName, String templateCode, Map<String,Object> paramMap){
        try {
            return FreeMarkerTemplateUtils.processTemplateIntoString(new Template(templateName, templateCode, freeMarkerConfig.getConfiguration()), paramMap);
        } catch (IOException e) {
            log.error("GenTableService ::: processTemplateCodeIntoString :: io error ",e);
            throw new BaseException("${templateName}模版代码生成异常",e);
        }catch (TemplateException e){
            log.error("GenTableService ::: processTemplateCodeIntoString :: template error ",e);
            throw new BaseException("${templateName}模版代码生成异常: ${e.getFTLInstructionStack()}",e);
        }

    }



}
